package cn.stylefeng.guns.sys.modular.system.service;

import cn.stylefeng.guns.base.auth.context.LoginContextHolder;
import cn.stylefeng.guns.sys.core.constant.cache.CacheKey;
import cn.stylefeng.guns.sys.core.exception.BusException;
import cn.stylefeng.guns.sys.core.exception.enums.BusExceptionEnum;
import cn.stylefeng.guns.sys.core.properties.SysConfig;
import cn.stylefeng.guns.sys.core.util.DDUtil;
import cn.stylefeng.guns.sys.core.util.DateUtil;
import cn.stylefeng.guns.sys.core.util.FileUtils;
import cn.stylefeng.guns.sys.core.util.RedisUtils;
import cn.stylefeng.guns.sys.modular.system.entity.BtyBxd;
import cn.stylefeng.guns.sys.modular.system.entity.BtyBxdDingding;
import cn.stylefeng.guns.sys.modular.system.entity.BtyBxda;
import cn.stylefeng.guns.sys.modular.system.mapper.*;
import cn.stylefeng.guns.sys.modular.system.model.DDBxdMx;
import cn.stylefeng.guns.sys.modular.system.model.DdBxdDetail;
import cn.stylefeng.guns.sys.modular.system.model.DdBxdlParam;
import cn.stylefeng.guns.sys.modular.system.model.params.ZxChengbenParam;
import cn.stylefeng.guns.sys.modular.system.model.result.BtyBxdDingdingResult;
import cn.stylefeng.guns.sys.modular.system.model.result.DDEmpResult;
import cn.stylefeng.guns.sys.modular.system.model.result.MdzxResult;
import cn.stylefeng.roses.core.reqres.response.ErrorResponseData;
import cn.stylefeng.roses.core.reqres.response.ResponseData;
import cn.stylefeng.roses.core.reqres.response.SuccessResponseData;
import cn.stylefeng.roses.core.util.ToolUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiProcessinstanceGetRequest;
import com.dingtalk.api.request.OapiProcessinstanceListidsRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiProcessinstanceGetResponse;
import com.dingtalk.api.response.OapiProcessinstanceListidsResponse;
import com.taobao.api.ApiException;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class DDService {
    private static final Logger log = LoggerFactory.getLogger(DDService.class);

    private static final String AUTO_IMPORTTIME="00 25 0 * * ?";//每日0.25执行

    private static final String URL_DDgettoken="https://oapi.dingtalk.com/gettoken";
    private static final String DD_APPKEY="dingvvrsivpeg6bqm4qb";
    private static final String DD_APPSECRET="wly_IoMnP0QXZzaWh-NiSOUxZJgpmDzZeKyy6LPQMktm9iO65RfO14LVYUliokL-";
    private static final String DD_BASE= "https://oapi.dingtalk.com/";

    private static final String SHOWDATA_LIST ="bxdDetaillist";
    private static final String PARAMKEY="param";
    private static final String BXDLIST_KEY="bxdlist";
    private static final String BXDALIST_KEY="bxdalist";
    private static final String CWRYMAP_KEY="cwryMap";
    private static final String DMMAP_KEY="dmMap";
    private static final String BANKMAP_KEY="bankMap";
    private static final String DETAILMAP_KEY="detailMap";
    private static final String SYNC_STARTBXD ="importData";//根据提交时间同步的数据
    private static final String SYNC_FINISH="importFinishData";//根据完成时间同步的数据
    private static final String SCHDUED_AUTOIMPORT="schdued_importData";//定时器导入的数据
    public static final String SYMBOL="@";

    public Map commData= new HashMap();
    public Map userBxdData= new HashMap();



    @Resource
    private BtyBxdMapper btyBxdMapper;
    @Resource
    private BtyBxdaMapper btyBxdaMapper;
    @Resource
    private BtyBrandMapper btyBrandMapper;
    @Resource
    private DingdingEmpMapper dingdingEmpMapper;
    @Resource
    private SysConfig sysConfig;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private BtyBxdaService btyBxdaService;

    @Resource
    private BtyBxdDingdingMapper btyBxdDingdingMapper;
    @Resource
    private ZxChengbenService zxChengbenService;
    @Resource
    private DDUtil dDUtil;

    /**
     * 获取token
     * @return
     * @throws ApiException
     */
    public String getToken() throws Exception {
        DingTalkClient client0 = new DefaultDingTalkClient(URL_DDgettoken);
        OapiGettokenRequest req0 = new OapiGettokenRequest();
        req0.setAppkey(DD_APPKEY);
        req0.setAppsecret(DD_APPSECRET);
        req0.setHttpMethod("GET");
        OapiGettokenResponse rsp0 = client0.execute(req0);

        Map result = (Map) JSON.parse(rsp0.getBody());
        Integer code = (Integer) result.get("errcode");
        if(code!=0){
            log.error("钉钉获取token失败,{}",result.get("errmsg"));
            return null;
        }
        return result.get("access_token").toString();
    }

    public ResponseData listBxdsNew(DdBxdlParam param) throws Exception {
        //查询时，从redis里面读取数据，并根据param进行查询
        List<BtyBxdDingdingResult> data=btyBxdDingdingMapper.list(param);
        return ResponseData.success(0,ResponseData.DEFAULT_SUCCESS_MESSAGE,data);
    }

    public ResponseData listBxdsNewkj(DdBxdlParam param) throws Exception {
        param.setKjName(LoginContextHolder.getContext().getUser().getName());
        List<BtyBxdDingdingResult> data=btyBxdDingdingMapper.list(param);
        return ResponseData.success(0,ResponseData.DEFAULT_SUCCESS_MESSAGE,data);
    }


    public ResponseData listBxds(DdBxdlParam param) throws Exception {
//        List<DdBxdDetail> data=null;//Map<String, Object>
        Map ddBxdData = (Map) userBxdData.get(LoginContextHolder.getContext().getUserId());
        DdBxdlParam oldparam=null;
        if(ToolUtil.isNotEmpty(ddBxdData)){
            oldparam = (DdBxdlParam) ddBxdData.get(PARAMKEY);
        }
        //获取bxd中已存在的钉钉id集合
        Set<String> bornvals = btyBxdMapper.listBornval();

        /*if(ToolUtil.isEmpty(param.getStartTime())){
            return ErrorResponseData.error(1,"开始时间条件不能为空");
        }

        if(oldparam==null ||!param.getStartTime().equals(oldparam.getStartTime())||!param.getEndTime().equals(oldparam.getEndTime())){
            //时间不同，则调取钉钉接口查询，其他条件在缓存查询
            ResponseData datarsp = getDDData(param);
            if(datarsp.getCode()==ResponseData.DEFAULT_ERROR_CODE){
                return ErrorResponseData.error(datarsp.getMessage());
            }
            if(ToolUtil.isNotEmpty(datarsp.getData())){
                data=(List<DdBxdDetail>)datarsp.getData();
            }
        }else{
//            Map ddBxdData = (Map) userBxdData.get(LoginContextHolder.getContext().getUserId());
            Object list = ddBxdData.get(DATAKEY);
            data=(List<DdBxdDetail>)list;
        }*/
//        param.setStartTime(DateUtil.getNowDate()+" 00:00:00");
        String startTime = param.getWcStartTime();
        String endTime = param.getWcEndTime();
        Date startDate = DateUtil.getDateByStr(startTime);
        Date endDate = DateUtil.getTimeToDate(endTime+" 23:59:59");
        //从缓存获取符合日期的审批单id
        Map<Object, Object> hmget = redisUtils.hmget(CacheKey.DD_FINISH_DATA);
        Set<Map.Entry<Object, Object>> entries = hmget.entrySet();
        List<String> wantList = new ArrayList<>();
        for(Map.Entry<Object, Object> entry:entries){
            Object key = entry.getKey();
            Set<String> value = (Set<String>) entry.getValue();
            Date curtDate = DateUtil.getDateByStr(key.toString());
            if((curtDate.after(startDate) || curtDate.equals(startDate)) && curtDate.before(endDate)){//筛选符合日期的数据
                wantList.addAll(value);
            }
        }

        log.info("获取报销单详情开始...");
        //获取token
        String token="";
        token = (String) redisUtils.get(CacheKey.DINGDING_TOKEN);
        if(ToolUtil.isEmpty(token)){
            token = getToken();
            if(ToolUtil.isNotEmpty(token)){
                redisUtils.set(CacheKey.DINGDING_TOKEN,token,7200);
            }
        }
        if(ToolUtil.isEmpty(token)){
            log.error("钉钉获取token失败");
            return ErrorResponseData.error(1,"钉钉获取token失败");
        }

        long d1=System.currentTimeMillis();
        List<DdBxdDetail> data=new ArrayList<>();//需要展示的列表
        Map<String,DdBxdDetail> detailMap=new HashMap<>();//查看详情时的bxdmap
        List<DdBxdDetail> finishData = new ArrayList<>();//根据完成时间 需要同步时的list
        for (String id : wantList) {
            ResponseData rsp = getDetailById(token, id);
            if (rsp.getData() != null) {
                DdBxdDetail detail = JSON.parseObject(rsp.getData().toString(),DdBxdDetail.class);
                detail.setBxdid(id);
                data.add(detail);
                detailMap.put(id,detail);
                //再完成的单子中挑选同意报销的单子,进行同步
                if("agree".equals(detail.getResult())){
                    finishData.add(detail);
                }

            }
        }

        long d2=System.currentTimeMillis();
        log.info("获取报销单详情结束.用时{}:{}",(d2-d1)/1000/60,(d2-d1)/1000%60);



        /*ResponseData datarsp = getDDData(param);
        if(datarsp.getCode()==ResponseData.DEFAULT_ERROR_CODE){
            return ErrorResponseData.error(datarsp.getMessage());
        }
        if(ToolUtil.isNotEmpty(datarsp.getData())){
            data=(List<DdBxdDetail>)datarsp.getData();
        }*/

//放入缓存
        Map<String,Object> tempData= new HashMap();
        tempData.put(SHOWDATA_LIST, data);
//        ddBxdData.put(PARAMKEY, param);
        tempData.put(DETAILMAP_KEY, detailMap);
        tempData.put(SYNC_FINISH, finishData);
        userBxdData.put(LoginContextHolder.getContext().getUserId(),tempData);

        String userName = param.getTijiaoren();//提交人
        String st = param.getSt();//状态
        String shopName = param.getShopName();//点名
        List<DdBxdDetail> newdata=new ArrayList<>();
        if(data!=null){
            for(DdBxdDetail bxd:data){
                if(ToolUtil.isNotEmpty(param.getSt())){//状态
                    if(!param.getSt().equals(bxd.getStatus())){
                        continue;
                    }
                }
                if(ToolUtil.isNotEmpty(param.getTijiaoren())){//提交人
                    String title = bxd.getTitle();
                    if(title.indexOf(param.getTijiaoren())<0){
                        continue;
                    }
                }
                if(ToolUtil.isNotEmpty(shopName)){
                    String dm = bxd.getOriginatorDeptName();
                    if(dm.indexOf(shopName)<0){
                        continue;
                    }
                }
                if(bornvals.contains(bxd.getBxdid())){
                    bxd.setSync("1");
                }
                newdata.add(bxd);

            }
        }
        return ResponseData.success(0,ResponseData.DEFAULT_SUCCESS_MESSAGE,newdata);
    }

    /**
     * 同步数据
     * @param param
     * @return
     * @throws Exception
     */
    public ResponseData cacheLastDaysData(DdBxdlParam param) throws Exception {
        //开始时间 和 结束时间 之间超过100天
        //时间转化
        String startTime = param.getWcStartTime() +" 00:00:00";
        String endTime = param.getWcEndTime() + " 23:59:59";;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        String startStr = simpleDateFormat.format(dayStart);
        Date startTimeD = simpleDateFormat.parse(startTime);
        Date endTimeD = simpleDateFormat.parse(endTime);

        QueryWrapper<BtyBxdDingding> wrapper=new QueryWrapper<BtyBxdDingding>();
        wrapper.between("finish_time",startTimeD, endTimeD);

        List<BtyBxdDingding> ddList = btyBxdDingdingMapper.selectList(wrapper);
        for(BtyBxdDingding dd:ddList){
            Long id = dd.getId();
            ddbxdSync(id.toString());
        }
        return  SuccessResponseData.success();
    }
    public ResponseData cacheLastDaysDataBak(DdBxdlParam param) throws Exception {
        //获取上次更新日期
        String lastTimestr = (String) redisUtils.get(CacheKey.DD_LASTUPDATE);
        Date lastTime=null;
        long start=0L;
        String lastUpdateDate="";//上次更新日期
        if(ToolUtil.isNotEmpty(lastTimestr)){
            //拉取上次时间之后的数据
            lastTime = DateUtil.getTimeToDate(lastTimestr);
            start=lastTime.getTime()-5000;//倒推5秒
            lastUpdateDate=lastTimestr.substring(0,10);
        }else{
            //默认拉取昨天的数据
            String t1 = DateUtil.addDate(DateUtil.getNowDate(), -1);
            start = DateUtil.getDateByStr(t1).getTime();//钉钉获取报销单的开始时间戳 当前日期之前的days天
        }

        List<DdBxdDetail> data=null;
        //获取token
        String token="";
        token = (String) redisUtils.get(CacheKey.DINGDING_TOKEN);
        if(ToolUtil.isEmpty(token)){
            token = getToken();
            if(ToolUtil.isNotEmpty(token)){
                redisUtils.set(CacheKey.DINGDING_TOKEN,token,7200);
            }
        }
        if(ToolUtil.isEmpty(token)){
            log.error("钉钉获取token失败");
            return ErrorResponseData.error(1,"钉钉获取token失败");
        }
        Long cursor=0L;
        List<String> ids=new LinkedList<>();
        List<DdBxdDetail> bxdlist = new ArrayList<>();
        String now = DateUtil.getNow();
        ResponseData responseData = listDdbxdIds(start, token, cursor,ids);
        if (ToolUtil.isNotEmpty(responseData.getData())) {

            ids = (List<String>) responseData.getData();
            log.info("一共获取{}个报销单id", ids.size());
            //从缓存获取未完成的单子id
            List<String> lGet = redisUtils.lGetT(CacheKey.DD_NOFINISH_DATA,0,-1);
            if(ToolUtil.isNotEmpty(lGet)){
                ids.addAll(lGet);
            }

            //根据id获取报销单详情
            log.info("获取报销单详情开始...");
            long d1 = System.currentTimeMillis();
            for (String id : ids) {
                ResponseData rsp = getDetailById(token, id);
                if (rsp.getData() != null) {
                    DdBxdDetail detail = JSON.parseObject(rsp.getData().toString(), DdBxdDetail.class);
                    detail.setBxdid(id);
                    bxdlist.add(detail);
                }
            }
            long d2 = System.currentTimeMillis();
            log.info("获取报销单详情结束.用时{}:{}", (d2 - d1) / 1000 / 60, (d2 - d1) / 1000 % 60);

            Map<String,Object> dateMap=new HashMap<>();//记录完成的单子，按照日期分
            List<Object> newFinishNo=new LinkedList<>();//记录未完成的单子
            List<DdBxdDetail> finishSyncData = new ArrayList<>();//根据完成时间 需要同步时的list
            Map<String,DdBxdDetail> detailMap=new HashMap<>();//查看详情时的bxdmap
            for(DdBxdDetail d : bxdlist){
                String finishTime = d.getFinishTime();//2020-11-26 14:10:34
                detailMap.put(d.getBxdid(),d);
                if(ToolUtil.isNotEmpty(finishTime)){
                    /*Date finishDate= DateUtil.getTimeToDate(finishTime);
                    if(ToolUtil.isNotEmpty(lastTime)){
                        //如果报销单完成时间在上次更新时间之前，则不予更新
                        if(finishDate.before(lastTime)){
                            log.debug("完成时间在上次更新时间之前，则不予更新：{}",finishTime);
                            continue;
                        }
                    }*/
                    String date = finishTime.substring(0, 10);//取日期
                    Set<String> idset = (Set<String>) dateMap.get(date);
                    if(ToolUtil.isEmpty(idset)){
                        idset = new HashSet<>();
                    }
                    idset.add(d.getBxdid());
                    dateMap.put(date,idset);
                    //再完成的单子中挑选同意报销的单子,进行同步
                    if("agree".equals(d.getResult())){
                        finishSyncData.add(d);
                    }
                }else{
                    newFinishNo.add(d.getBxdid());
                }
            }
            if(ToolUtil.isNotEmpty(lastUpdateDate)){
                Map<Object, Object> hmget = redisUtils.hmget(CacheKey.DD_FINISH_DATA);
                Set<String> old = (Set<String>) hmget.get(lastUpdateDate);
                Set<String> news = (Set<String>) dateMap.get(lastUpdateDate);
                if(ToolUtil.isNotEmpty(news)&&ToolUtil.isNotEmpty(old)){
                    news.addAll(old);//追加旧数据，防止覆盖
                }
            }

            /*Map<String,Object> tempData= new HashMap();
            tempData.put(DETAILMAP_KEY, detailMap);
            tempData.put(SHOWDATA_LIST, bxdlist);
//            tempData.put(SYNC_FINISH, finishSyncData);

            if(ToolUtil.isNotEmpty(LoginContextHolder.getContext().getUserId())){
                userBxdData.put(LoginContextHolder.getContext().getUserId(),tempData);
            }*/

            //放入redis
            redisUtils.hmset(CacheKey.DD_FINISH_DATA,dateMap);
            redisUtils.lSetList(CacheKey.DD_NOFINISH_DATA,newFinishNo);
            redisUtils.set(CacheKey.DD_LASTUPDATE,now);
            log.info("缓存更新钉钉报销单完成id结束.");
            return ResponseData.success(0,ResponseData.DEFAULT_SUCCESS_MESSAGE,bxdlist);
        }else{
            return ResponseData.success(0,ResponseData.DEFAULT_SUCCESS_MESSAGE,null);
        }
    }


    /**
     * 从钉钉获取数据
     * @param param
     * @return
     * @throws Exception
     */
    public ResponseData getDDData(DdBxdlParam param) throws Exception{
        //获取token
        String token="";
        token = (String) redisUtils.get(CacheKey.DINGDING_TOKEN);
        if(ToolUtil.isEmpty(token)){
            token = getToken();
            if(ToolUtil.isNotEmpty(token)){
                redisUtils.set(CacheKey.DINGDING_TOKEN,token,7200);
            }
        }
        if(ToolUtil.isEmpty(token)){
            return ErrorResponseData.error("钉钉获取token失败");
        }
        Long start = 0L;
        Long end = null;
        if(ToolUtil.isEmpty(param.getWcStartTime())){
            String yearstoday = DateUtil.getYearstoday(new Date());
            start=DateUtil.getDateByStr(yearstoday).getTime();
        }else{
            start=DateUtil.getDateByStr(param.getWcStartTime()).getTime();
        }
        if(ToolUtil.isNotEmpty(param.getWcEndTime())){
            end=DateUtil.getTimeToDate(param.getWcEndTime()+" 23:59:59").getTime();
        }
        //获取保险单id
        ResponseData result = listIdsByDD(start,end, token,0L);
        if(result.getCode()!=ResponseData.DEFAULT_SUCCESS_CODE){
            return ErrorResponseData.error(result.getMessage());
        }
        if(ToolUtil.isNotEmpty(result.getData())) {
            Map<String, Object> rs = (Map<String, Object>) result.getData();
            List<String> listall = (List<String>) rs.get("list");
            Object next = rs.get("next_cursor");
            while (ToolUtil.isNotEmpty(next)) {
//                log.info("===" + next);
                Long nextpage = Long.valueOf(next.toString());
                ResponseData responseData = listIdsByDD(start, end, token, nextpage);
                if (ToolUtil.isNotEmpty(responseData.getData())) {
                    rs = (Map<String, Object>) responseData.getData();
                    List<String> list = (List<String>) rs.get("list");
                    listall.addAll(list);
                    next = rs.get("next_cursor");
                }
            }
            List<DdBxdDetail> bxdlist = new ArrayList<>();
            List<DdBxdDetail> testlist = new ArrayList<>();
            List<DdBxdDetail> finishData = new ArrayList<>();
            Map<String,DdBxdDetail> detailMap=new HashMap<>();
            //根据id获取报销单详情
            for (String id : listall) {
                ResponseData rsp = getDetailById(token, id);
                if (rsp.getData() != null) {
                    DdBxdDetail detail = JSON.parseObject(rsp.getData().toString(),DdBxdDetail.class);
                    detail.setBxdid(id);
                    bxdlist.add(detail);
                    detailMap.put(id,detail);
                    if("agree".equals(detail.getResult())){
                        testlist.add(detail);
                    }
                    if(ToolUtil.isNotEmpty(detail.getFinishTime())){//审批单全部节点完成时间,流程走完时间
                        finishData.add(detail);
                    }
                }
            }
            //放入缓存
            Map ddBxdData= new HashMap();
            ddBxdData.put(SHOWDATA_LIST, bxdlist);
            ddBxdData.put(PARAMKEY, param);
            ddBxdData.put(DETAILMAP_KEY, detailMap);
            ddBxdData.put(SYNC_STARTBXD, testlist);
            ddBxdData.put(SYNC_FINISH, finishData);
            userBxdData.put(LoginContextHolder.getContext().getUserId(),ddBxdData);
            return ResponseData.success(0,ResponseData.DEFAULT_SUCCESS_MESSAGE,bxdlist);
        }
        return ErrorResponseData.error(null);
    }


    /**
     * 从钉钉获取报销单id列表
     * @param start
     * @param end
     * @param token
     * @param cursor
     * @return
     * @throws ApiException
     */
    public ResponseData listIdsByDD(Long start,Long end,String token,Long cursor) throws ApiException {
        DingTalkClient client = new DefaultDingTalkClient(DD_BASE+"topapi/processinstance/listids");
        OapiProcessinstanceListidsRequest req = new OapiProcessinstanceListidsRequest();
        req.setProcessCode("PROC-EF6YV4VRN2-QVI9F5L3RDB8N17V543Y2-5QWAMUMI-L51");
        req.setStartTime(start);//startTime参数，这个时间距离当前时间不能超过120天
        if(end!=null){
            req.setEndTime(end);//结束时间。不传该参数则默认取当前时间。
        }
        req.setSize(20L);
//        req.setUseridList("manager1,manager2");
        req.setCursor(cursor);
        OapiProcessinstanceListidsResponse rsp = client.execute(req, token);

        Map map = (Map) JSON.parse(rsp.getBody());
        Integer  errcode= (Integer) map.get("errcode");
        if(errcode!=0){
            return ErrorResponseData.error("钉钉获取报销单id列表失败,"+map.get("errmsg").toString());
        }
        Map<String,Object> rs = (Map<String, Object>) map.get("result");

        return ResponseData.success(rs);
    }

    /**
     * 获取某个时间段的所有IDS
     * @param start
     * @param end
     * @param token
     * @return
     * @throws ApiException
     * by zwf 20201129
     */
    public List<String> listIdsByDDAll(Long start,Long end,String token) throws ApiException {
        List<String> all =  new ArrayList<String>();
        Long cursor = 0L;
        Object next_cursor="0L";
        do{
            ResponseData r = listIdsByDD(start,end,token,cursor);
            Map<String, Object> rs = (Map<String, Object>) r.getData();
            List<String> list = (List<String>) rs.get("list");
            all.addAll(list);
            next_cursor = rs.get("next_cursor");
            if(next_cursor!=null){
                cursor = Long.parseLong(next_cursor.toString());
            }
        }while (next_cursor!=null);
        return  all;
    }

    /**
     * 根据id获取保险单详情
     * @param token
     * @param bxdId
     * @return
     * @throws ApiException
     */
    public ResponseData getDetailById(String token,String bxdId) throws ApiException {
        DingTalkClient client = new DefaultDingTalkClient(DD_BASE+"topapi/processinstance/get");
        OapiProcessinstanceGetRequest request = new OapiProcessinstanceGetRequest();
        request.setProcessInstanceId(bxdId);
        OapiProcessinstanceGetResponse rsp = client.execute(request,token);

        Map map = (Map) JSON.parse(rsp.getBody());
        Integer  errcode= (Integer) map.get("errcode");
        if(errcode!=0){
            return ErrorResponseData.error("钉钉获取报销单详情失败,"+map.get("errmsg").toString());
        }
        Map<String,Object> rs = (Map<String, Object>) map.get("process_instance");
        return ResponseData.success(rs);
    }



//    @Scheduled(cron = AUTOIMPORTTIME)
    public void autoImportData() throws Exception {
        log.info("报销单自动同步开始...");
        long l1=System.currentTimeMillis();
        String days = sysConfig.getAutoImportDays();
        if(Long.valueOf(days)<0L){
            log.info("报销单定时任务暂停...{}",days);
            return;
        }
        String t1 = DateUtil.addDate(DateUtil.getNowDate(), -1*Long.valueOf(days));
        long start = DateUtil.getDateByStr(t1).getTime();//钉钉获取报销单的开始时间戳 当前日期之前的days天

        //获取token
        String token="";
        token = (String) redisUtils.get(CacheKey.DINGDING_TOKEN);
        if(ToolUtil.isEmpty(token)){
            token = getToken();
            if(ToolUtil.isNotEmpty(token)){
                redisUtils.set(CacheKey.DINGDING_TOKEN,token,7200);
            }
        }
        if(ToolUtil.isEmpty(token)){
            log.error("钉钉获取token失败");
        }
        Long cursor=0L;
        List<String> ids=new ArrayList<>();
//        List<DdBxdDetail> bxdlist = new ArrayList<>();
        List<DdBxdDetail> synclist = new ArrayList<>();//需要同步的数据
        ResponseData responseData = listDdbxdIds(start, token, cursor,ids);

        List<String> errors=new ArrayList<>();//错误信息
        List<String> hasSync=new ArrayList<>();//已同步信息
        List<String> sucinfo=new ArrayList<>();//同步成功的信息

        if (ToolUtil.isNotEmpty(responseData.getData())) {
            ids = (List<String>) responseData.getData();
            log.info("一共获取{}个报销单id",ids.size());
            //根据id获取报销单详情
            log.info("获取报销单详情开始...");
            long d1=System.currentTimeMillis();
            for (String id : ids) {
                ResponseData rsp = getDetailById(token, id);
                if (rsp.getData() != null) {
                    DdBxdDetail detail = JSON.parseObject(rsp.getData().toString(),DdBxdDetail.class);
                    detail.setBxdid(id);
//                    bxdlist.add(detail);
//                    detailMap.put(id,detail);
                    if("agree".equals(detail.getResult())){
                        synclist.add(detail);
                    }
                }
            }
            long d2=System.currentTimeMillis();
            log.info("获取报销单详情结束.用时{}:{}",(d2-d1)/1000/60,(d2-d1)/1000%60);
//            ddBxdData.put(SCHDUED_AUTOIMPORT, synclist);
            //获取财务人员列表
            List<Map<String, String>> cwrylist = btyBrandMapper.getCwry();
            Map<String, String> cwryMap=new HashMap<>();
            Map<String, String> dmMap=new HashMap<>();
            for(Map<String, String> m:cwrylist){
                String yaoma = m.get("yaoma");
                String cwry = m.get("cwry");
                String dm = m.get("dm");
                cwryMap.put(yaoma,cwry);
                dmMap.put(yaoma,dm);
            }
            commData.put(CWRYMAP_KEY,cwryMap);
            commData.put(DMMAP_KEY,dmMap);
            //获取bxd中已存在的钉钉id集合
            Set<String> bornvals = btyBxdMapper.listBornval();
            //获取银行简称行号
            List<Map<String, String>> banks=btyBxdaService.getBankCodeAll();
            Map<String, String> bankmap=new HashMap<>();
            for(Map<String, String> m:banks){
                String ver = m.get("ver");
                String bh = m.get("bh");
                bankmap.put(ver,bh);
            }
            commData.put(BANKMAP_KEY,bankmap);

            //获取装修会计钉钉id,id
            String bxdZxkjs = sysConfig.getBxdZxkj();

            if(synclist==null || synclist.isEmpty()){
                log.error("可同步的报销单数据为空，无需同步");
            }

            List<BtyBxda> bxdalist = new ArrayList<>();//需要insert的bxda数据
            List<BtyBxd> bxdlist =new ArrayList<>();//需要insert的bxd数据



            for(DdBxdDetail bxd:synclist){
                String bxdid = bxd.getBxdid();
                if(bornvals.contains(bxdid)){
                    //已同步的不再同步
                    String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 已同步,无需重复同步;";
//                    log.debug(err);
                    hasSync.add(err);
                    continue;
                }

                List<DdBxdDetail.TasksDTO> tasks = bxd.getTasks();
                //过滤掉 闫丽群的装修单，此处不做处理
                boolean zhuangxiu=false;
                if(ToolUtil.isNotEmpty(bxdZxkjs)){
                    zhuangxiu = isZhuangxiu(tasks,bxdZxkjs);
                }
                if(zhuangxiu){
                    String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 为装修报销单,舍弃;";
                    log.debug(err);
//                errors.add(err);
                    continue;
                }

                List<DdBxdDetail.FormComponentValuesDTO> formComponentValues = bxd.getFormComponentValues();
                //获取报销单数据
                DdBxdDetail.FormComponentValuesDTO bxddata = formComponentValues.get(0);//数据
                DdBxdDetail.FormComponentValuesDTO picData = formComponentValues.get(1);//图片
                DdBxdDetail.FormComponentValuesDTO AttchData = formComponentValues.get(2);//附件
                String value = bxddata.getValue();
                //明细-1个报销条目一个记录
                List<DDBxdMx> bxdMxs = JSON.parseArray(value, DDBxdMx.class);
                for(DDBxdMx mx:bxdMxs){
                    List<DDBxdMx.RowValueDTO> rowValue = mx.getRowValue();
                    DDBxdMx.RowValueDTO text0=(DDBxdMx.RowValueDTO)(rowValue.get(0));
                    DDBxdMx.RowValueDTO text1=(DDBxdMx.RowValueDTO)(rowValue.get(1));//店面
                    DDBxdMx.RowValueDTO text2=(DDBxdMx.RowValueDTO)(rowValue.get(2));//总金额
                    DDBxdMx.RowValueDTO text3=(DDBxdMx.RowValueDTO)(rowValue.get(3));//类别
                    DDBxdMx.RowValueDTO text4=null;
                    if(rowValue.size()>=5){
                        text4=(DDBxdMx.RowValueDTO)(rowValue.get(4));
                    }
                    String bxrname = text0.getValue();//报销人 不一定是提交人
                    String dm = text1.getValue();//dm
                    String je = text2.getValue();//报销金额(总)
                    String fymx = (text4==null?"":text4.getValue());//费用明细
                    String yaoma = getNum(dm);
                    String shopname=getShopName(yaoma);
                    if(ToolUtil.isEmpty(yaoma)){
                        //店面没有yaoma 直接舍弃 ，并返回页面
                        String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 没有门店号码, 无法同步;";
//                        log.debug(err);
                        errors.add(err);
                        continue;
                    }
                    if(ToolUtil.isEmpty(shopname)){
                        //店面没有yaoma 直接舍弃 ，并返回页面
                        String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 根据门店号未找到相应的门店, 无法同步;";
//                        log.debug(err);
                        errors.add(err);
                        continue;
                    }
                    if(!isNumeric(je)){
                        String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 报销金额输入有误, 无法同步;";
//                        log.debug(err);
                        errors.add(err);
                        continue;
                    }
                    String yhk=getYhk(fymx);
                    if(ToolUtil.isEmpty(yhk)){//没有银行卡 直接舍弃
                        String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 没有银行卡, 无法同步;";
//                        log.debug(err);
                        errors.add(err);
                        continue;
                    }
                    CostMx co=new CostMx();
                    co.setCostMoney(je);
                    co.setCostName(text3.getValue());
                    co.setYhcard(yhk);
                    co.setDm(shopname.trim());
                    co.setYaoma(yaoma);
                    co.setBxr(bxrname);
                    co.setDetail(fymx);
                    co.setBornId(bxdid);
                    co.setCreateUserId(1L);

                    //组装表数据
                    generateData(co,bxdalist,bxdlist);

                    String suclog=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+",同步成功;";
                    sucinfo.add(suclog);
                }
            }

            sucinfo.addAll(errors);
            sucinfo.addAll(hasSync);

            //保存数据
            if(ToolUtil.isNotEmpty(bxdalist)){
                //每150条保存一次
                for(int i=0;i<bxdalist.size();){
                    int i2=(i+150);
                    i2=i2>bxdalist.size()?bxdalist.size():i2;
                    List<BtyBxda> sub = bxdalist.subList(i, i2);
                    int bxdaNum = btyBxdaMapper.batchAdd(sub);
                    for(int bxdi=0;bxdi<i2;bxdi++){
                        bxdlist.get(bxdi).setBtyBxdaId(bxdalist.get(bxdi).getId().toString());
                    }

                    List<BtyBxd> bxdsub = bxdlist.subList(i, i2);
                    int bxdNum = btyBxdMapper.batchAdd(bxdsub);
                    log.debug("bxd(a) save index [{}-{}]",i, i2);
                    i=i2;
                    if(bxdaNum!=bxdNum){ //理论上bxdaNum=bxdNum
                        log.error("报销单定时任务插入数据有误：{}",BusExceptionEnum.ERR_INSERT.getMessage());
                        throw new BusException(BusExceptionEnum.ERR_INSERT);
                    }
                }
               log.info("报销单定时任务-成功插入{}条数据",bxdalist.size());
            }else{
                log.error("报销单定时任务-未找到可同步的数据");
            }
        }

        //记录日志保存到文件
        boolean b = FileUtils.write2File(sysConfig.getAutoImportLogPath(), "ddbxd-scheduler"+DateUtil.getNowDate()+".log", sucinfo);

        long l2=System.currentTimeMillis();
        long used=(l2-l1)/1000;
        log.info("报销单自动同步结束.用时：{}:{}",used/60,used%60);
    }

    /**获取钉钉ids*/
    public ResponseData listDdbxdIds(long start,String token,Long cursor,List<String> ids) throws ApiException {
        log.debug("page-size:{}",cursor);
        ResponseData responseData = listIdsByDD(start, null, token, cursor);
        if (ToolUtil.isNotEmpty(responseData.getData())) {
            Map<String, Object> rs = (Map<String, Object>) responseData.getData();
            List<String> list = (List<String>) rs.get("list");
            Object next_cursor = rs.get("next_cursor");
            ids.addAll(list);
            if(ToolUtil.isNotEmpty(next_cursor)){
                listDdbxdIds(start,token,Long.valueOf(next_cursor.toString()),ids);
            }
        }else{
            log.error("定时任务获取钉钉id异常，cause：{}",responseData.getMessage());
            return responseData;
        }
        return SuccessResponseData.success(ids);
    }


    @Transactional(rollbackFor = Exception.class)
    public ResponseData copyBxd(DdBxdlParam param) throws ParseException {
        Map ddBxdData = (Map) userBxdData.get(LoginContextHolder.getContext().getUserId());
        Object list = ddBxdData.get(SYNC_STARTBXD);
        //获取财务人员列表
        List<Map<String, String>> cwrylist = btyBrandMapper.getCwry();
        Map<String, String> cwryMap=new HashMap<>();
        Map<String, String> dmMap=new HashMap<>();
        for(Map<String, String> m:cwrylist){
            String yaoma = m.get("yaoma");
            String cwry = m.get("cwry");
            String dm = m.get("dm");
            cwryMap.put(yaoma,cwry);
            dmMap.put(yaoma,dm);
        }
        commData.put(CWRYMAP_KEY,cwryMap);
        commData.put(DMMAP_KEY,dmMap);
        //获取bxd中已存在的钉钉id集合
        Set<String> bornvals = btyBxdMapper.listBornval();
        //获取银行简称行号
        List<Map<String, String>> banks=btyBxdaService.getBankCodeAll();
        Map<String, String> bankmap=new HashMap<>();
        for(Map<String, String> m:banks){
            String ver = m.get("ver");
            String bh = m.get("bh");
            bankmap.put(ver,bh);
        }
        commData.put(BANKMAP_KEY,bankmap);

        //获取装修会计钉钉id,id
        String bxdZxkjs = sysConfig.getBxdZxkj();

        List<DdBxdDetail> data=(List<DdBxdDetail>)list;
        if(data==null || data.isEmpty()){
           return ResponseData.error(1,"数据为空，无需同步");
        }
        List<BtyBxda> bxdalist = new ArrayList<>();
        List<BtyBxd> bxdlist =new ArrayList<>();

        List<String> errors=new ArrayList<>();
        List<String> hasSync=new ArrayList<>();

        for(DdBxdDetail bxd:data){
            BtyBxd btyBxd = new BtyBxd();
            String bxdid = bxd.getBxdid();
            if(bornvals.contains(bxdid)){
                //已同步的不再同步
                String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 已同步,无需重复同步;";
                log.debug(err);
                hasSync.add(err);
                continue;
            }
            List<DdBxdDetail.TasksDTO> tasks = bxd.getTasks();
            //过滤掉 闫丽群的装修单，此处不做处理
            boolean zhuangxiu=false;
            if(ToolUtil.isNotEmpty(bxdZxkjs)){
                zhuangxiu = isZhuangxiu(tasks,bxdZxkjs);
            }
            if(zhuangxiu){
                String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 为装修报销单,舍弃;";
                log.debug(err);
//                errors.add(err);
                continue;
            }

            List<DdBxdDetail.FormComponentValuesDTO> formComponentValues = bxd.getFormComponentValues();
            //获取报销单数据
            DdBxdDetail.FormComponentValuesDTO bxddata = formComponentValues.get(0);//数据
            DdBxdDetail.FormComponentValuesDTO picData = formComponentValues.get(1);//图片
            DdBxdDetail.FormComponentValuesDTO AttchData = formComponentValues.get(2);//附件

                String value = bxddata.getValue();
                //明细-1个报销条目一个记录
                List<DDBxdMx> bxdMxs = JSON.parseArray(value, DDBxdMx.class);
                for(DDBxdMx mx:bxdMxs){

                    List<DDBxdMx.RowValueDTO> rowValue = mx.getRowValue();
                    DDBxdMx.RowValueDTO text0=(DDBxdMx.RowValueDTO)(rowValue.get(0));
                    DDBxdMx.RowValueDTO text1=(DDBxdMx.RowValueDTO)(rowValue.get(1));//店面
                    DDBxdMx.RowValueDTO text2=(DDBxdMx.RowValueDTO)(rowValue.get(2));//总金额
                    DDBxdMx.RowValueDTO text3=(DDBxdMx.RowValueDTO)(rowValue.get(3));//类别
                    DDBxdMx.RowValueDTO text4=null;
                   if(rowValue.size()>=5){
                       text4=(DDBxdMx.RowValueDTO)(rowValue.get(4));
                   }
                    String bxrname = text0.getValue();//报销人 不一定是提交人
                    String dm = text1.getValue();//dm
                    String je = text2.getValue();//报销金额(总)
                    String fymx = (text4==null?"":text4.getValue());//费用明细
                    String yaoma = getNum(dm);
                    String shopname=getShopName(yaoma);
                    if(ToolUtil.isEmpty(yaoma)){
                        //店面没有yaoma 直接舍弃 ，并返回页面
                        String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 没有门店号码, 无法同步;";
                        log.debug(err);
                        errors.add(err);
                        continue;
                    }
                    if(ToolUtil.isEmpty(shopname)){
                        //店面没有yaoma 直接舍弃 ，并返回页面
                        String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 根据门店号未找到相应的门店, 无法同步;";
                        log.debug(err);
                        errors.add(err);
                        continue;
                    }
                    if(!isNumeric(je)){
                        String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 报销金额输入有误, 无法同步;";
                        log.debug(err);
                        errors.add(err);
                        continue;
                    }
                    String yhk=getYhk(fymx);
                    if(ToolUtil.isEmpty(yhk)){//没有银行卡 直接舍弃
                        String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 没有银行卡, 无法同步;";
                        log.debug(err);
                        errors.add(err);
                        continue;
                    }
                    CostMx co=new CostMx();
                    co.setCostMoney(je);
                    co.setCostName(text3.getValue());
                    co.setYhcard(yhk);
                    co.setDm(shopname.trim());
                    co.setYaoma(yaoma);
                    co.setBxr(bxrname);
                    co.setDetail(fymx);
                    co.setBornId(bxdid);
                    co.setCreateUserId(LoginContextHolder.getContext().getUserId());

                    //组装表数据
                    generateData(co,bxdalist,bxdlist);
                }

            //

        }
        errors.addAll(hasSync);
        //保存数据
        if(ToolUtil.isNotEmpty(bxdalist)){
            //每150条保存一次
            for(int i=0;i<bxdalist.size();){
                int i2=(i+150);
                i2=i2>bxdalist.size()?bxdalist.size():i2;
                List<BtyBxda> sub = bxdalist.subList(i, i2);
                int bxdaNum = btyBxdaMapper.batchAdd(sub);
                for(int bxdi=0;bxdi<i2;bxdi++){
                    bxdlist.get(bxdi).setBtyBxdaId(bxdalist.get(bxdi).getId().toString());
                }

                List<BtyBxd> bxdsub = bxdlist.subList(i, i2);
                int bxdNum = btyBxdMapper.batchAdd(bxdsub);
                log.debug("bxd(a) save index [{}-{}]",i, i2);
                i=i2;
                if(bxdaNum!=bxdNum){ //理论上bxdaNum=bxdNum
                    throw new BusException(BusExceptionEnum.ERR_INSERT);
                }
            }
            //更新同步结果
//            Map ddBxdData = (Map) userBxdData.get(LoginContextHolder.getContext().getUserId());
            List<DdBxdDetail> showlist = (List<DdBxdDetail>) ddBxdData.get(SHOWDATA_LIST);
            for(BtyBxd item:bxdlist){
                for(DdBxdDetail d:showlist){
                    if(d.getBxdid().equals(item.getBornVal())){
                        d.setSync("1");
                        break;
                    }
                }
            }
            ddBxdData.put(SHOWDATA_LIST,showlist);
            userBxdData.put(LoginContextHolder.getContext().getUserId(),ddBxdData);

            return SuccessResponseData.success(errors);
        }
        return ResponseData.error(1,"未找到可同步的数据",errors);
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseData btnCopyFinish(DdBxdlParam param) throws ParseException {
        Map ddBxdData = (Map) userBxdData.get(LoginContextHolder.getContext().getUserId());
        Object list = ddBxdData.get(SYNC_FINISH);
        List<DdBxdDetail> data=(List<DdBxdDetail>)list;
        if(data==null || data.isEmpty()){
            return ResponseData.error(1,"数据为空，无需同步");
        }

        //获取财务人员列表
        List<Map<String, String>> cwrylist = btyBrandMapper.getCwry();
        Map<String, String> cwryMap=new HashMap<>();
        Map<String, String> dmMap=new HashMap<>();
        for(Map<String, String> m:cwrylist){
            String yaoma = m.get("yaoma");
            String cwry = m.get("cwry");
            String dm = m.get("dm");
            cwryMap.put(yaoma,cwry);
            dmMap.put(yaoma,dm);
        }
        commData.put(CWRYMAP_KEY,cwryMap);
        commData.put(DMMAP_KEY,dmMap);
        //获取bxd中已存在的钉钉id集合
        Set<String> bornvals = btyBxdMapper.listBornval();
        //获取银行简称行号
        List<Map<String, String>> banks=btyBxdaService.getBankCodeAll();
        Map<String, String> bankmap=new HashMap<>();
        for(Map<String, String> m:banks){
            String ver = m.get("ver");
            String bh = m.get("bh");
            bankmap.put(ver,bh);
        }
        commData.put(BANKMAP_KEY,bankmap);

        //获取装修会计钉钉id,id
        String bxdZxkjs = sysConfig.getBxdZxkj();


        List<BtyBxda> bxdalist = new ArrayList<>();
        List<BtyBxd> bxdlist =new ArrayList<>();

        List<String> errors=new ArrayList<>();
        List<String> hasSync=new ArrayList<>();

        for(DdBxdDetail bxd:data){
            BtyBxd btyBxd = new BtyBxd();
            String bxdid = bxd.getBxdid();
            if(bornvals.contains(bxdid)){
                //已同步的不再同步
                String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 已同步,无需重复同步;";
                log.debug(err);
                hasSync.add(err);
                continue;
            }
            List<DdBxdDetail.TasksDTO> tasks = bxd.getTasks();
            //过滤掉 闫丽群的装修单，此处不做处理
            boolean zhuangxiu=false;
            if(ToolUtil.isNotEmpty(bxdZxkjs)){
                zhuangxiu = isZhuangxiu(tasks,bxdZxkjs);
            }
            if(zhuangxiu){
                String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 为装修报销单,舍弃;";
                log.debug(err);
//                errors.add(err);
                continue;
            }

            List<DdBxdDetail.FormComponentValuesDTO> formComponentValues = bxd.getFormComponentValues();
            //获取报销单数据
            DdBxdDetail.FormComponentValuesDTO bxddata = formComponentValues.get(0);//数据
            DdBxdDetail.FormComponentValuesDTO picData = formComponentValues.get(1);//图片
            DdBxdDetail.FormComponentValuesDTO AttchData = formComponentValues.get(2);//附件

            String value = bxddata.getValue();
            //明细-1个报销条目一个记录
            List<DDBxdMx> bxdMxs = JSON.parseArray(value, DDBxdMx.class);
            for(DDBxdMx mx:bxdMxs){

                List<DDBxdMx.RowValueDTO> rowValue = mx.getRowValue();
                DDBxdMx.RowValueDTO text0=(DDBxdMx.RowValueDTO)(rowValue.get(0));
                DDBxdMx.RowValueDTO text1=(DDBxdMx.RowValueDTO)(rowValue.get(1));//店面
                DDBxdMx.RowValueDTO text2=(DDBxdMx.RowValueDTO)(rowValue.get(2));//总金额
                DDBxdMx.RowValueDTO text3=(DDBxdMx.RowValueDTO)(rowValue.get(3));//类别
                DDBxdMx.RowValueDTO text4=null;
                if(rowValue.size()>=5){
                    text4=(DDBxdMx.RowValueDTO)(rowValue.get(4));
                }
                String bxrname = text0.getValue();//报销人 不一定是提交人
                String dm = text1.getValue();//dm
                String je = text2.getValue();//报销金额(总)
                String fymx = (text4==null?"":text4.getValue());//费用明细
                String yaoma = getNum(dm);
                String shopname=getShopName(yaoma);
                if(ToolUtil.isEmpty(yaoma)){
                    //店面没有yaoma 直接舍弃 ，并返回页面
                    String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 没有门店号码, 无法同步;";
                    log.debug(err);
                    errors.add(err);
                    continue;
                }
                if(ToolUtil.isEmpty(shopname)){
                    //店面没有yaoma 直接舍弃 ，并返回页面
                    String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 根据门店号未找到相应的门店, 无法同步;";
                    log.debug(err);
                    errors.add(err);
                    continue;
                }
                if(!isNumeric(je)){
                    String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 报销金额输入有误, 无法同步;";
                    log.debug(err);
                    errors.add(err);
                    continue;
                }
                String yhk=getYhk(fymx);
                if(ToolUtil.isEmpty(yhk)){//没有银行卡 直接舍弃
                    String err=bxd.getTitle()+"，提交时间："+bxd.getCreateTime()+", 没有银行卡, 无法同步;";
                    log.debug(err);
                    errors.add(err);
                    continue;
                }
                CostMx co=new CostMx();
                co.setCostMoney(je);
                co.setCostName(text3.getValue());
                co.setYhcard(yhk);
                co.setDm(shopname.trim());
                co.setYaoma(yaoma);
                co.setBxr(bxrname);
                co.setDetail(fymx);
                co.setBornId(bxdid);
                co.setCreateUserId(LoginContextHolder.getContext().getUserId());

                //组装表数据
                generateData(co,bxdalist,bxdlist);
            }

            //

        }
        errors.addAll(hasSync);
        //保存数据
        if(ToolUtil.isNotEmpty(bxdalist)){
            //每150条保存一次
            for(int i=0;i<bxdalist.size();){
                int i2=(i+150);
                i2=i2>bxdalist.size()?bxdalist.size():i2;
                List<BtyBxda> sub = bxdalist.subList(i, i2);
                int bxdaNum = btyBxdaMapper.batchAdd(sub);
                for(int bxdi=0;bxdi<i2;bxdi++){
                    bxdlist.get(bxdi).setBtyBxdaId(bxdalist.get(bxdi).getId().toString());
                }

                List<BtyBxd> bxdsub = bxdlist.subList(i, i2);
                int bxdNum = btyBxdMapper.batchAdd(bxdsub);
                log.debug("bxd(a) save index [{}-{}]",i, i2);
                i=i2;
                if(bxdaNum!=bxdNum){ //理论上bxdaNum=bxdNum
                    throw new BusException(BusExceptionEnum.ERR_INSERT);
                }
            }
            //更新同步结果
//            Map ddBxdData = (Map) userBxdData.get(LoginContextHolder.getContext().getUserId());
            List<DdBxdDetail> showlist = (List<DdBxdDetail>) ddBxdData.get(SHOWDATA_LIST);
            for(BtyBxd item:bxdlist){
                for(DdBxdDetail d:showlist){
                    if(d.getBxdid().equals(item.getBornVal())){
                        d.setSync("1");
                        break;
                    }
                }
            }
            ddBxdData.put(SHOWDATA_LIST,showlist);
            userBxdData.put(LoginContextHolder.getContext().getUserId(),ddBxdData);

            return SuccessResponseData.success(errors);
        }
        return ResponseData.error(1,"未找到可同步的数据",errors);
    }

    /** 判断是否装修单*/
    public boolean isZhuangxiu(List<DdBxdDetail.TasksDTO> tasks,String bxdZxkjs){
        DdBxdDetail.TasksDTO tasksDTO = tasks.get(tasks.size() - 1);
        for(String ddid : bxdZxkjs.split(",")){
            if(tasksDTO.getUserid().equals(ddid)){
                return true;
            }
        }
        return false;
    }
    /**
     * 获取店名
     * @param yaoma
     * @return
     */
    private String getShopName(String yaoma) {
        Map<String, String> dmmap = (Map<String, String>) commData.get(DMMAP_KEY);
        return dmmap.get(yaoma);
    }

    String getYhk(String str){
        str=str.trim();
        String str2="";
        String yhcard="";
        if(str != null && !"".equals(str)){
            for(int i=0;i<str.length();i++){
                if((str.charAt(i)>=48 && str.charAt(i)<=57)||str.charAt(i)==46){
                    str2+=str.charAt(i);
                }else if(str.charAt(i)==32){//空格

                }else {
                    str2+=SYMBOL;
                }
            }

        }
        String[] split = str2.split(SYMBOL);
        for(String s:split){
            if(s.length()>11){//银行卡
                yhcard=s;
            }
        }
        return yhcard;
    }

    /**
     * 组装数据
     */
    private void generateData(CostMx co,List<BtyBxda> bxdalist,List<BtyBxd> bxdlist) throws ParseException {

        String bank = getYhCode(co.getYhcard());
        Map<String, String> cwmap = (Map<String, String>) commData.get(CWRYMAP_KEY);
        Map<String, String> bankmap = (Map<String, String>) commData.get(BANKMAP_KEY);
        BtyBxd bxd = new BtyBxd();
        bxd.setZy(co.getDetail());//
        bxd.setZcje(co.getCostMoney());
        bxd.setYaoMa(co.getYaoma());
        String stampNow1 = DateUtil.getStampNow();
        int rid1 = (int) ((Math.random()*9+1)*100);//随机取100-999
        bxd.setBh(stampNow1+rid1);//编号
//        bxd.setBtyBxdaId("");//报销档案外键
        bxd.setCreateUser(co.getCreateUserId());
        bxd.setCreateTime(DateUtil.getTimeToDate(co.getFinishTime()));
        bxd.setBz("");//备注
//            bxd.setPicture("");
        bxd.setStatus(0);//审核状态 0未审核1已审核[-1店长 ,-2督导,-3营运经理] 0财务
//            btyBxd.setFlag(0);//报销状态 默认0未报销 1已报销
        bxd.setBorn(2);//来源：1 手工录入；2 钉钉；3 小程序
        bxd.setBornVal(co.getBornId());
        bxdlist.add(bxd);

        BtyBxda bxda = new BtyBxda();
        String stampNow2 = DateUtil.getStampNow();
        int rid2 = (int) ((Math.random()*9+1)*100);//随机取100-999
        bxda.setBh(stampNow2+rid2);
        bxda.setYhk(co.getYhcard());
        bxda.setXm(co.getBxr());
        bxda.setBank(bank);
        bxda.setBz(bankmap.get(bank));
        bxda.setCreateUser(co.getCreateUserId());
        bxda.setCreateTime(DateUtil.getTimeToDate(co.getFinishTime()));
        bxda.setGhs(co.getCostName());
        bxda.setDm(co.getDm());
        bxda.setKj(cwmap.get(co.getYaoma()));//会计
        bxdalist.add(bxda);
    }

    /**
     *  通过银行卡号获取银行简称
     * @param yhk
     * @return
     */
    public String getYhCode(String yhk){
        RestTemplate restTemplate = new RestTemplate();
        String url = "https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardNo="+yhk+"&cardBinCheck=true";
        String content = restTemplate.getForObject(url, String.class);
        JSONObject jsonObject = JSONObject.parseObject(content);
        String bank="";
        if(ToolUtil.isNotEmpty(jsonObject.get("bank"))){
            bank = jsonObject.get("bank").toString();
        }
        return bank;
    }

    /**
     *  是否是钱数
     * @return
     */
    public boolean isNumeric(String str){
        Pattern pattern = Pattern.compile("([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9])");
        Matcher isNum = pattern.matcher(str);
        if( !isNum.matches() ){
            return false;
        }
        return true;
    }

    /**
     * 提取数字
     * @param str
     * @return
     */
    public static String getJine(String str){
        str=str.trim();
        String str2="";
        if(str != null && !"".equals(str)){
            for(int i=0;i<str.length();i++){
                if((str.charAt(i)>=48 && str.charAt(i)<=57)||str.charAt(i)==46){
                    str2+=str.charAt(i);
                }else {
                    str2+=SYMBOL;
                }
            }

        }
        return str2;
    }

    /**
     * 提取数字
     * @param str
     * @return
     */
    public static String getNum(String str){
        str=str.trim();
        String str2="";
        if(str != null && !"".equals(str)){
            for(int i=0;i<str.length();i++){
                if((str.charAt(i)>=48 && str.charAt(i)<=57)||str.charAt(i)==46){
                    str2+=str.charAt(i);
                }
            }

        }
        return str2;
    }

    /**
     * 获取报销单事项和金额
     * @param str
     * @return
     */
    public CostMx[] getCost(String str){
        //筛选数字包括 '元’,
        String yuanStr = getJine(str);
        //分离数据，找出费用金额
        String[] split = yuanStr.split(SYMBOL);
        List<String> yuanlist=new ArrayList<>();
//        int tiaoshu=0;//费用条数
        String yhcard="";
        for(String s:split){
            if(s.contains("元")){
                yuanlist.add(s);
//                tiaoshu++;
            }else if(s.length()>10){//银行卡
                yhcard=s;
            }
        }
        String str2=str;
        CostMx[] items=new CostMx[yuanlist.size()];
        for(int i=0;i<yuanlist.size();i++){
//            BtyBxd btyBxd = new BtyBxd();
            //分别找出 金额 出现的位置，分析出费用事项
            String y=yuanlist.get(i);
            int ii = str2.indexOf(y);
            String costname=str2.substring(0,ii);
            str2=str2.substring(ii+y.length());
//            btyBxd.setZcje(yuanAry[i].substring(0,yuanAry[i].length()-1));
//            btyBxd.setZy(costname);
//            String item=+SYMBOL+yuanAry[i].substring(0,yuanAry[i].length()-1);
            CostMx item=new CostMx();
            item.setCostName(costname);
            item.setCostMoney(y.substring(0,y.length()-1));
            item.setYhcard(yhcard);
            items[i]=item;
        }
        return items;
    }

    public DdBxdDetail getById(String id) throws Exception {
        BtyBxdDingding dd = btyBxdDingdingMapper.selectById(Long.parseLong(id));
        String json = dd.getJson();
        DdBxdDetail bxd = JSON.parseObject(json,DdBxdDetail.class);
        bxd.setBxdid(dd.getBxdid());
        List<DdBxdDetail.TasksDTO> tasks = bxd.getTasks();
        Collections.sort(tasks);
        for(DdBxdDetail.TasksDTO t:tasks){
            t.setUserName(getDDName(t.getUserid()));
        }
        return bxd;
    }

    @XxlJob("dddsbxdDataJob")
//    @Scheduled(cron = AUTO_IMPORTTIME)
    public void autoDownLoad() throws Exception {
        log.info("钉钉定时获取最新报销单开始...");
        DdBxdlParam param= new DdBxdlParam();
        String nowDate = DateUtil.getNowDate();
        //同步前3天
        param.setTjStartTime(DateUtil.addDate(nowDate,-3));
        param.setTjEndTime(DateUtil.addDate(nowDate,-1));
        pullFromDingding(param);

        log.info("钉钉定时获取最新报销单结束.");
    }


    /**
     * 单个报销单同步
     * @param id
     * @return
     * @throws ParseException
     */
    public ResponseData ddbxdSync(String id) throws ParseException{
        BtyBxdDingding dd = btyBxdDingdingMapper.selectById(Long.parseLong(id));
        String bxdid = dd.getBxdid();

        if(ToolUtil.isEmpty(bxdid)){
            return ErrorResponseData.error(1,"缺少必要参数");
        }
        String json = dd.getJson();

        DdBxdDetail bxd=JSON.parseObject(json,DdBxdDetail.class);
        if("COMPLETED".equals(bxd.getStatus())){
            if("refuse".equals(bxd.getResult())){
                return ErrorResponseData.error(1,"已拒绝，不能同步");
            }
        }else{
            return ErrorResponseData.error(1,"流程未完成，不能同步");
        }

//        BtyBxd btyBxd = new BtyBxd();
        //获取bxd中已存在的钉钉id集合
//        Set<String> bornvals = btyBxdMapper.listBornval();
//        if(bornvals.contains(bxdid)){
        if("1".equals(dd.getSync())){
            //已同步的不再同步
            return ErrorResponseData.error(1,"已同步,不能重复同步;");
        }

        //获取装修会计钉钉id,id
        List<DdBxdDetail.TasksDTO> tasks = bxd.getTasks();
        String bxdZxkjs = sysConfig.getBxdZxkj();
        boolean zhuangxiu=false;
        if(ToolUtil.isNotEmpty(bxdZxkjs)){
            zhuangxiu = isZhuangxiu(tasks,bxdZxkjs);
        }
       /* if(zhuangxiu){
            return ErrorResponseData.error(1,"此单为装修报销单，无法同步;");
        }*/

        //获取财务人员列表和门店数据
        List<Map<String, String>> cwrylist = btyBrandMapper.getCwry();
        Map<String, String> cwryMap=new HashMap<>();
        Map<String, String> dmMap=new HashMap<>();
        for(Map<String, String> m:cwrylist){
            String yaoma = m.get("yaoma");
            String cwry = m.get("cwry");
            String dm = m.get("dm");
            cwryMap.put(yaoma,cwry);
            dmMap.put(yaoma,dm);
        }
        commData.put(CWRYMAP_KEY,cwryMap);
        commData.put(DMMAP_KEY,dmMap);

        //获取银行简称行号
        List<Map<String, String>> banks=btyBxdaService.getBankCodeAll();
        Map<String, String> bankmap=new HashMap<>();
        for(Map<String, String> m:banks){
            String ver = m.get("ver");
            String bh = m.get("bh");
            bankmap.put(ver,bh);
        }
        commData.put(BANKMAP_KEY,bankmap);

        List<DdBxdDetail.FormComponentValuesDTO> formComponentValues = bxd.getFormComponentValues();
        //获取报销单数据
        DdBxdDetail.FormComponentValuesDTO bxddata = formComponentValues.get(0);//数据
        DdBxdDetail.FormComponentValuesDTO picData = formComponentValues.get(1);//图片
        DdBxdDetail.FormComponentValuesDTO AttchData = formComponentValues.get(2);//附件

        String value = bxddata.getValue();
        //明细-1个报销条目一个记录
        List<DDBxdMx> bxdMxs = JSON.parseArray(value, DDBxdMx.class);

        List<BtyBxda> bxdalist = new ArrayList<>();
        List<BtyBxd> bxdlist =new ArrayList<>();
        List<ZxChengbenParam> chengbenlist =new ArrayList<>();
        for(DDBxdMx mx:bxdMxs){
            List<DDBxdMx.RowValueDTO> rowValue = mx.getRowValue();
            String bxrname = getbxdValue(rowValue, "报销人员");
            String dm = getbxdValue(rowValue, "申请店面");
            String je = getbxdValue(rowValue, "报销金额");
            String type = getbxdValue(rowValue, "报销类别");
            String fymx = getbxdValue(rowValue, "费用明细");
            String yaoma = getNum(dm);
            String shopname="";
            if(!isNumeric(je)){
                return ErrorResponseData.error(1,"报销金额输入有误, 无法同步;");
            }


            if(zhuangxiu) {//装修单
                ZxChengbenParam cb = new ZxChengbenParam();
                cb.setBeizhu("");
                cb.setRiqi(DateUtil.getNowDate());
                MdzxResult md = zxChengbenService.queryMdMohu(dm);
                if(md!=null){
                    cb.setBtyZxMdzxId(md.getBtyZxMdzxId());
                    cb.setBtyBrandIdView(md.getBtyBrandIdView());
                }else {
                    cb.setBeizhu(dm);
                }
                cb.setBtyyBxdaIdView(bxrname);
                cb.setZongjia(new BigDecimal(je));
//                cb.setSrcType("2");
//                cb.setSrcValue(bxdid);
//                cb.setZhaiyao();
//                cb.setYhKahao();

                cb.setZhaiyao(type);

                cb.setDaKuan("1");
                cb.setLeixing("1");
                cb.setBeizhu(cb.getBeizhu()+fymx);
                chengbenlist.add(cb);
            }else{
                String yhk=getYhk(fymx);
                if(ToolUtil.isEmpty(yhk)){//没有银行卡 直接舍弃
                    return ErrorResponseData.error(1,"没有银行卡, 无法同步;");
                }
                //装修单不判断店面
                if(ToolUtil.isEmpty(yaoma)){
                    return ErrorResponseData.error(1,"没有门店号码, 无法同步;");
                }
                shopname=getShopName(yaoma);
                if(ToolUtil.isEmpty(shopname)){
                    return ErrorResponseData.error(1,"根据门店号未找到相应的门店, 无法同步;");
                }
                CostMx co=new CostMx();
                co.setCostMoney(je);
                co.setCostName(type);
                co.setYhcard(yhk);
                co.setDm(shopname.trim());
                co.setYaoma(yaoma);
                co.setBxr(bxrname);
                co.setDetail(fymx);
                co.setBornId(bxdid);
                co.setCreateUserId(LoginContextHolder.getContext().getUserId());
                co.setFinishTime(bxd.getFinishTime());

                //组装表数据
                generateData(co,bxdalist,bxdlist);
            }
        }
        //保存数据
        if(zhuangxiu){
            for(ZxChengbenParam cb:chengbenlist){
                zxChengbenService.add(cb);
            }
            //更新同步结果
            dd.setSync("1");
            btyBxdDingdingMapper.updateById(dd);
            return SuccessResponseData.success();
        }else{
            if(ToolUtil.isNotEmpty(bxdalist)){
                int bxdaNum = btyBxdaMapper.batchAdd(bxdalist);
                for(int bxdi=0;bxdi<bxdalist.size();bxdi++){
                    bxdlist.get(bxdi).setBtyBxdaId(bxdalist.get(bxdi).getId().toString());
                }
                int bxdNum = btyBxdMapper.batchAdd(bxdlist);
                if(bxdaNum!=bxdNum){//理论上bxdaNum=bxdNum
                    throw new BusException(BusExceptionEnum.ERR_INSERT);
                }
                //更新同步结果
                dd.setSync("1");
                btyBxdDingdingMapper.updateById(dd);
                return SuccessResponseData.success();
            }
        }
        return ResponseData.error(1,"未找到可同步的数据");
    }

    private String getbxdValue(List<DDBxdMx.RowValueDTO> rowValue,String label){
        for(DDBxdMx.RowValueDTO ddr : rowValue){
            if(label.equals(ddr.getLabel())){
                return ddr.getValue();
            }
        }
        return "";
    }

    public ResponseData ddbxdSyncBak(String bxdid) throws ParseException{
        if(ToolUtil.isEmpty(bxdid)){
            return ErrorResponseData.error(1,"缺少必要参数");
        }
        DdBxdDetail bxd=null;
        Map ddBxdData = (Map) userBxdData.get(LoginContextHolder.getContext().getUserId());
        Map<String,DdBxdDetail> detailMap = (Map<String, DdBxdDetail>) ddBxdData.get(DETAILMAP_KEY);
        if(ToolUtil.isNotEmpty(detailMap)){
            bxd = detailMap.get(bxdid);
        }
        if(ToolUtil.isEmpty(bxd)){
            return ErrorResponseData.error(1,"同步失败");
        }
        if("COMPLETED".equals(bxd.getStatus())){
            if("refuse".equals(bxd.getResult())){
                return ErrorResponseData.error(1,"已拒绝，不能同步");
            }
        }else{
            return ErrorResponseData.error(1,"流程未完成，不能同步");
        }

//        BtyBxd btyBxd = new BtyBxd();
        //获取bxd中已存在的钉钉id集合
        Set<String> bornvals = btyBxdMapper.listBornval();
        if(bornvals.contains(bxdid)){
            //已同步的不再同步
            return ErrorResponseData.error(1,"已同步,不能重复同步;");
        }


        //过滤掉 闫丽群的装修单，此处不做处理
        //获取装修会计钉钉id,id
        List<DdBxdDetail.TasksDTO> tasks = bxd.getTasks();
        String bxdZxkjs = sysConfig.getBxdZxkj();
        boolean zhuangxiu=false;
        if(ToolUtil.isNotEmpty(bxdZxkjs)){
            zhuangxiu = isZhuangxiu(tasks,bxdZxkjs);
        }
        if(zhuangxiu){
            return ErrorResponseData.error(1,"此单为装修报销单，无法同步;");
        }

        //获取财务人员列表和门店数据
        List<Map<String, String>> cwrylist = btyBrandMapper.getCwry();
        Map<String, String> cwryMap=new HashMap<>();
        Map<String, String> dmMap=new HashMap<>();
        for(Map<String, String> m:cwrylist){
            String yaoma = m.get("yaoma");
            String cwry = m.get("cwry");
            String dm = m.get("dm");
            cwryMap.put(yaoma,cwry);
            dmMap.put(yaoma,dm);
        }
        commData.put(CWRYMAP_KEY,cwryMap);
        commData.put(DMMAP_KEY,dmMap);

        //获取银行简称行号
        List<Map<String, String>> banks=btyBxdaService.getBankCodeAll();
        Map<String, String> bankmap=new HashMap<>();
        for(Map<String, String> m:banks){
            String ver = m.get("ver");
            String bh = m.get("bh");
            bankmap.put(ver,bh);
        }
        commData.put(BANKMAP_KEY,bankmap);

        List<DdBxdDetail.FormComponentValuesDTO> formComponentValues = bxd.getFormComponentValues();
        //获取报销单数据
        DdBxdDetail.FormComponentValuesDTO bxddata = formComponentValues.get(0);//数据
        DdBxdDetail.FormComponentValuesDTO picData = formComponentValues.get(1);//图片
        DdBxdDetail.FormComponentValuesDTO AttchData = formComponentValues.get(2);//附件

        String value = bxddata.getValue();
        //明细-1个报销条目一个记录
        List<DDBxdMx> bxdMxs = JSON.parseArray(value, DDBxdMx.class);

        List<BtyBxda> bxdalist = new ArrayList<>();
        List<BtyBxd> bxdlist =new ArrayList<>();
        for(DDBxdMx mx:bxdMxs){
            List<DDBxdMx.RowValueDTO> rowValue = mx.getRowValue();
            DDBxdMx.RowValueDTO text0=(DDBxdMx.RowValueDTO)(rowValue.get(0));
            DDBxdMx.RowValueDTO text1=(DDBxdMx.RowValueDTO)(rowValue.get(1));//店面
            DDBxdMx.RowValueDTO text2=(DDBxdMx.RowValueDTO)(rowValue.get(2));//总金额
            DDBxdMx.RowValueDTO text3=(DDBxdMx.RowValueDTO)(rowValue.get(3));//类别
            DDBxdMx.RowValueDTO text4=null;
            if(rowValue.size()>=5){
                text4=(DDBxdMx.RowValueDTO)(rowValue.get(4));
            }
            String bxrname = text0.getValue();//报销人 不一定是提交人
            String dm = text1.getValue();//dm
            String je = text2.getValue();//报销金额(总)
            String fymx = (text4==null?"":text4.getValue());//费用明细
            String yaoma = getNum(dm);
            if(ToolUtil.isEmpty(yaoma)){
                return ErrorResponseData.error(1,"没有门店号码, 无法同步;");
            }
            String shopname=getShopName(yaoma);
            if(ToolUtil.isEmpty(shopname)){
                return ErrorResponseData.error(1,"根据门店号未找到相应的门店, 无法同步;");
            }
            if(!isNumeric(je)){
                return ErrorResponseData.error(1,"报销金额输入有误, 无法同步;");
            }
            String yhk=getYhk(fymx);
            if(ToolUtil.isEmpty(yhk)){//没有银行卡 直接舍弃
                return ErrorResponseData.error(1,"没有银行卡, 无法同步;");
            }
            CostMx co=new CostMx();
            co.setCostMoney(je);
            co.setCostName(text3.getValue());
            co.setYhcard(yhk);
            co.setDm(shopname.trim());
            co.setYaoma(yaoma);
            co.setBxr(bxrname);
            co.setDetail(fymx);
            co.setBornId(bxdid);
            co.setCreateUserId(LoginContextHolder.getContext().getUserId());

            //组装表数据
            generateData(co,bxdalist,bxdlist);
        }

        //保存数据
        if(ToolUtil.isNotEmpty(bxdalist)){
            int bxdaNum = btyBxdaMapper.batchAdd(bxdalist);
            for(int bxdi=0;bxdi<bxdalist.size();bxdi++){
                bxdlist.get(bxdi).setBtyBxdaId(bxdalist.get(bxdi).getId().toString());
            }
            int bxdNum = btyBxdMapper.batchAdd(bxdlist);

            if(bxdaNum!=bxdNum){//理论上bxdaNum=bxdNum
                throw new BusException(BusExceptionEnum.ERR_INSERT);
            }

            //更新同步结果
            List<DdBxdDetail> showlist = (List<DdBxdDetail>) ddBxdData.get(SHOWDATA_LIST);
            for(DdBxdDetail d:showlist){
                if(d.getBxdid().equals(bxdid)){
                    d.setSync("1");
                    break;
                }
            }
            ddBxdData.put(SHOWDATA_LIST,showlist);
            userBxdData.put(LoginContextHolder.getContext().getUserId(),ddBxdData);

            return SuccessResponseData.success();
        }
        return ResponseData.error(1,"未找到可同步的数据");
    }

    private String getDDName(String userid) throws Exception {
        /*List<DingdingEmp> emps = dingdingEmpMapper.listAll();
        for(DingdingEmp emp:emps){
            if(userid.equals(emp.getId())){
                return emp.getName();
            }
        }*/
        Map<String, String> kjs = getKJ();
        String name = kjs.get(userid);
        return ToolUtil.isEmpty(name)?userid:name;
    }

    /**
     * 拉取最新数据
     * @param param
     * @return
     */
    public ResponseData pullFromDingding(DdBxdlParam param){

        try{
            //开始时间 和 结束时间 之间超过100天
            log.info(param.getTjStartTime());
            log.info(param.getTjEndTime());
            //时间转化
            String startTime = param.getTjStartTime() +" 00:00:00";
            String endTime = param.getTjEndTime() + " 23:59:59";
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        String startStr = simpleDateFormat.format(dayStart);
            Date startTimeD = simpleDateFormat.parse(startTime);
            Date endTimeD = simpleDateFormat.parse(endTime);

            //根据开始结束和结束时间获取所有ID
            String token = dDUtil.getToken();

            log.info("获取时间段的所有IDS");
            List<String> idsArr = listIdsByDDAll(startTimeD.getTime(), endTimeD.getTime(), token);

            log.info("获取时间段的所有IDS end");
            BtyBxdDingding dd = null;
            for (String id : idsArr) {
                QueryWrapper<BtyBxdDingding> wrapper=new QueryWrapper<BtyBxdDingding>();
                wrapper.eq("dd_id",id);
                BtyBxdDingding ddnew = btyBxdDingdingMapper.selectOne(wrapper);
                //新增
                if(ddnew==null){
                    ResponseData rsp = getDetailById(token, id);
                    if (rsp.getData() != null) {
                        String json = rsp.getData().toString();
                        dd = jsonToBtyBxdDingding(id,json);
                        btyBxdDingdingMapper.insert(dd);
                    }
                }else {
                    if(ddnew.getSync()!=null && ddnew.getSync().equals("1")){
                        continue;
                    }
                    ResponseData rsp = getDetailById(token, id);
                    if (rsp.getData() != null) {
                        String json = rsp.getData().toString();
                        if(!ddnew.getJson().equals(json)){
                            dd = jsonToBtyBxdDingding(id,json);
                            dd.setId(ddnew.getId());
                            btyBxdDingdingMapper.updateById(dd);
                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return SuccessResponseData.success();
    }

      /**
     * 会计下载数据
     * @param param
     * @return
     */
    public ResponseData pullFromDingdingKj(DdBxdlParam param){
        try{
            //时间转化
            String nowDateStr = DateUtil.getNowDate();
            Date nowDate = DateUtil.getDateByStr(nowDateStr);
            Date datestart = DateUtil.getDateByStr(param.getTjStartTime());
            Date dateend = DateUtil.getTimeToDate(param.getTjEndTime()+" 23:59:59");
            QueryWrapper<BtyBxdDingding> qw = new QueryWrapper();
            List<String> idsall=new ArrayList<>();
            String token = dDUtil.getToken();
            if(dateend.before(nowDate)){//如果时间段在今天之前，则查数据库中的id
                qw.between("create_time",datestart,dateend);
                qw.eq("sync",0);
                qw.ne("status","TERMINATED");
                List<BtyBxdDingding> oldBxds = btyBxdDingdingMapper.selectList(qw);
                for(BtyBxdDingding d:oldBxds){
                    idsall.add(d.getBxdid());
                }
            }else if(nowDate.before(datestart)){//今后的数据 无法获取直接显示没数据
                return SuccessResponseData.success();
            }else {
                qw.between("create_time",datestart,nowDate);
                qw.eq("sync",0);
                qw.ne("status","TERMINATED");
                List<BtyBxdDingding> oldBxds = btyBxdDingdingMapper.selectList(qw);
                for(BtyBxdDingding d:oldBxds){
                    idsall.add(d.getBxdid());
                }
                //钉钉获取今天日期最新的id
                log.info("获取今天最新的IDS");
                List<String> newids = listIdsByDDAll(nowDate.getTime(), null, token);
                log.info("获取今天最新的IDS end");
                idsall.addAll(newids);
            }

            BtyBxdDingding dd = null;
            for (String id : idsall) {
                QueryWrapper<BtyBxdDingding> wrapper=new QueryWrapper<BtyBxdDingding>();
                wrapper.eq("dd_id",id);
                BtyBxdDingding ddnew = btyBxdDingdingMapper.selectOne(wrapper);
                //新增
                if(ddnew==null){
                    ResponseData rsp = getDetailById(token, id);
                    if (rsp.getData() != null) {
                        String json = rsp.getData().toString();
                        dd = jsonToBtyBxdDingding(id,json);
                        btyBxdDingdingMapper.insert(dd);
                    }
                }else {
                    if(ddnew.getSync()!=null && ddnew.getSync().equals("1")){
                        continue;
                    }
                    ResponseData rsp = getDetailById(token, id);
                    if (rsp.getData() != null) {
                        String json = rsp.getData().toString();
                        if(!ddnew.getJson().equals(json)){
                            dd = jsonToBtyBxdDingding(id,json);
                            dd.setId(ddnew.getId());
                            btyBxdDingdingMapper.updateById(dd);
                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return SuccessResponseData.success();
    }

    private BtyBxdDingding jsonToBtyBxdDingding(String id,String json) throws Exception {
        BtyBxdDingding dd = null;
        DdBxdDetail detail = JSON.parseObject(json,DdBxdDetail.class);
        detail.setBxdid(id);
        dd = getEntity(detail);
        dd.setDd_id(id);
        dd.setJson(json);
        String st = dd.getStatus();
        if(st.equals("COMPLETED")){
            dd.setStatusView("审批通过");
        }else if(st.equals("refuse")){
            dd.setStatusView("审批拒绝");
        }else if(st.equals("TERMINATED")){
            dd.setStatusView("撤销");
        }else if(st.equals("RUNNING")){
            dd.setStatusView("审批中");
        }else if(st.equals("NEW")){
            dd.setStatusView("新创建");
        }else {
            dd.setStatusView("");
        }
        String baoxiaoren = null;
        String zje = null;
        String kjs = null;
        //报销人
        List<DdBxdDetail.FormComponentValuesDTO> formComponentValues=detail.getFormComponentValues();
        for(DdBxdDetail.FormComponentValuesDTO d:formComponentValues){
            if(d.getComponentType().equals("TableField")){
                String value = d.getValue();
                JSONArray vjson = JSON.parseArray(value);
                for(int i=0;i<vjson.size();i++){
                    JSONObject vo = vjson.getJSONObject(i);
                    JSONArray rowValue = vo.getJSONArray("rowValue");
                    for(int j=0;j<rowValue.size();j++){
                        JSONObject rvt = rowValue.getJSONObject(j);
                        String label = rvt.getString("label");
                        if(label.equals("报销人员")){//巴
                            if(baoxiaoren==null){
                                baoxiaoren = rvt.getString("value");
                            }else {
                                baoxiaoren+=","+rvt.getString("value");
                            }
                        }
                        if(label.equals("申请店面")){
                            String deptName = dd.getOriginatorDeptName();
                            deptName+="-"+rvt.getString("value");
                            dd.setOriginatorDeptName(deptName);

                           /* String yaoma = getNum(deptName);
                            if(!commData.containsKey(CWRYMAP_KEY)){
                                initCwry();
                            }
                            Map<String, String> cwryMap = (Map<String, String>)commData.get(CWRYMAP_KEY);
                            String kj = cwryMap.get(yaoma);
                            if(kjs==null){
                                kjs=kj;
                            }else{
                                kjs+=","+kj;
                            }*/

                        }
                    }
                }
                String extValue = d.getExtValue();
                JSONObject evJson = JSON.parseObject(extValue);
                JSONArray statValue = evJson.getJSONArray("statValue");
                for(int i=0;i<statValue.size();i++){
                    JSONObject jO = statValue.getJSONObject(i);
                    String lv = jO.getString("label");
                    if(lv.equals("总报销金额")){
                        zje = jO.getString("num");
                    }
                }
                break;
            }
        }

        //获取会计
        List<DdBxdDetail.TasksDTO> tasks = detail.getTasks();
        Collections.sort(tasks);
        DdBxdDetail.TasksDTO lasttask = tasks.get(tasks.size() - 1);
        String userid = lasttask.getUserid();
        String finishtime = lasttask.getCreateTime();
        String ddName = getDDName(userid);
        if(ToolUtil.isNotEmpty(finishtime)){
            dd.setFinishTime(Timestamp.valueOf(finishtime));
        }
        dd.setBaoxiaoren(baoxiaoren);
        dd.setKj(ddName);
        if(zje==null||zje.equals("")){
            zje = "0.00";
        }
        dd.setZje(new BigDecimal(zje));
        return dd;
    }

    private void initCwry(){
        List<Map<String, String>> cwrylist = btyBrandMapper.getCwry();
        Map<String, String> cwryMap=new HashMap<>();
        Map<String, String> dmMap=new HashMap<>();
        for(Map<String, String> m:cwrylist){
            String yaoma = m.get("yaoma");
            String cwry = m.get("cwry");
            String dm = m.get("dm");
            cwryMap.put(yaoma,cwry);
            dmMap.put(yaoma,dm);
        }
        commData.put(CWRYMAP_KEY,cwryMap);
        commData.put(DMMAP_KEY,dmMap);
    }
    private BtyBxdDingding getEntity(DdBxdDetail param) {
        BtyBxdDingding entity = new BtyBxdDingding();
        ToolUtil.copyProperties(param, entity);
        return entity;
    }

    //DINGDING获取会计，每日更新一次
    public Map<String,String> getKJ() throws Exception {
        String todaydate=DateUtil.getNowDate();
        if(commData.containsKey("DDLASTCWRY")){
            Map<String,Object> cwdata = (Map<String, Object>) commData.get("DDLASTCWRY");
            if(cwdata.containsKey(todaydate)){
                return  (Map<String,String>)cwdata.get(todaydate);
            }else{
                cwdata.clear();
            }

        }
        List<DDEmpResult> ddEmpResults = dDUtil.queryDingdingEmpBydeptid(8124195L);
        Map<String,Object> cwdata = new HashMap<>();
        Map<String,String> cwryMap = new HashMap<>();
        for(DDEmpResult dr:ddEmpResults){
            cwryMap.put(dr.getUserid(),dr.getName());
        }
        cwdata.put(todaydate,cwryMap);
        commData.put("DDLASTCWRY",cwdata);
        return cwryMap;
    }


    final static SimpleDateFormat sdf = new SimpleDateFormat(
            "yyyy-MM-dd HH:mm:ss");

    final static String startTime = sdf.format(new Date());

    /**
     * IO密集型任务 = 一般为2*CPU核心数（常出现于线程中：数据库数据交互、文件上传下载、网络数据传输等等）
     * CPU密集型任务 = 一般为CPU核心数+1（常出现于线程中：复杂算法）
     * 混合型任务 = 视机器配置和复杂度自测而定
     */
    private static int corePoolSize = Runtime.getRuntime().availableProcessors();
    /**
     * public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
     *              TimeUnit unit,BlockingQueue<Runnable> workQueue)
     * corePoolSize用于指定核心线程数量
     * maximumPoolSize指定最大线程数
     * keepAliveTime和TimeUnit指定线程空闲后的最大存活时间
     * workQueue则是线程池的缓冲队列,还未执行的线程会在队列中等待
     * 监控队列长度，确保队列有界
     * 不当的线程池大小会使得处理速度变慢，稳定性下降，并且导致内存泄露。如果配置的线程过少，则队列会持续变大，消耗过多内存。
     * 而过多的线程又会 由于频繁的上下文切换导致整个系统的速度变缓——殊途而同归。队列的长度至关重要，它必须得是有界的，这样如果线程池不堪重负了它可以暂时拒绝掉新的请求。
     * ExecutorService 默认的实现是一个无界的 LinkedBlockingQueue。
     */
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(1000));

    static class Stats implements Callable<String> {
        String statsName;
        int runTime;
        List<Integer> ids = null;

        public Stats(String statsName,List<Integer> ids, int runTime) {
            this.statsName = statsName;
            this.runTime = runTime;
            this.ids=ids;
        }

        public String call() {
            try {
                System.out.println(statsName+ " do stats begin at "+ startTime);



                //模拟任务执行时间
                Thread.sleep(runTime);
                System.out.println(statsName + " do stats complete at "+ sdf.format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return call();
        }
    }

    class CostMx{
        public String costName;//事项
        public String costMoney;//金额
        public String yhcard;//银行卡
        public String dm;//
        public String yaoma;//yaoma
        public String bxr;//bxr报销人
        public String detail;//
        public String bornId;
        public Long createUserId;
        public String finishTime;

        public String getCostName() {
            return costName;
        }
        public void setCostName(String costName) {
            this.costName = costName;
        }
        public String getCostMoney() {
            return costMoney;
        }
        public void setCostMoney(String costMoney) {
            this.costMoney = costMoney;
        }
        public String getYhcard() {
            return yhcard;
        }
        public void setYhcard(String yhcard) {
            this.yhcard = yhcard;
        }
        public String getDm() {
            return dm;
        }
        public void setDm(String dm) {
            this.dm = dm;
        }
        public String getYaoma() {
            return yaoma;
        }
        public void setYaoma(String yaoma) {
            this.yaoma = yaoma;
        }
        public String getBxr() {
            return bxr;
        }
        public void setBxr(String bxr) {
            this.bxr = bxr;
        }
        public String getDetail() {
            return detail;
        }
        public void setDetail(String detail) {
            this.detail = detail;
        }
        public String getBornId() {
            return bornId;
        }
        public void setBornId(String bornId) {
            this.bornId = bornId;
        }
        public Long getCreateUserId() {
            return createUserId;
        }
        public void setCreateUserId(Long createUserId) {
            this.createUserId = createUserId;
        }
        public String getFinishTime() {
            return finishTime;
        }
        public void setFinishTime(String finishTime) {
            this.finishTime = finishTime;
        }
    }
}
